package util

import (
	"bytes"
	"ip_server/config"
	"database/sql"
	"encoding/json"
	"fmt"
	"math/rand"
	"os"
	"reflect"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-irain/logger"
	"golang.org/x/crypto/bcrypt"
)

// GenerateLogid 生成Logid
func GenerateLogid() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return strconv.FormatInt(time.Now().UnixNano(), 10) + fmt.Sprintf(":%d", r.Intn(10000))
}

// String interface 转string
func String(v interface{}) string {
	return fmt.Sprintf("%v", v)
}

// Int64 字符串转int64
func Int64(s string) int64 {
	if s == "" {
		return int64(0)
	}
	n, err := strconv.ParseInt(s, 10, 0)
	if err != nil {
		return 0
	}
	return n
}

// Int 字符转整形
func Int(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		return 0
	}
	return n
}

// GetRandomString 获取随机字符串
func GetRandomString(num int, str ...string) string {
	s := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	if len(str) > 0 {
		s = str[0]
	}
	l := len(s)
	r := rand.New(rand.NewSource(getRandSeek()))
	var buf bytes.Buffer
	for i := 0; i < num; i++ {
		x := r.Intn(l)
		buf.WriteString(s[x : x+1])
	}
	return buf.String()
}

var (
	randSeek = int64(1)
	l        sync.Mutex
)

func getRandSeek() int64 {
	l.Lock()
	if randSeek >= 100000000 {
		randSeek = 1
	}
	randSeek++
	l.Unlock()
	return time.Now().UnixNano() + randSeek
}

// PanicStack 崩溃日志
func PanicStack() {
	if err := recover(); err != nil {
		str := string(debug.Stack()) + "\n--------------------------------------\n"
		str += fmt.Sprintln(err) + "\n"
		file, err := os.OpenFile(config.GetConfig().MustValue("log", "path")+"/crash.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		defer file.Close()
		_, err = file.WriteString(str)
		if err != nil {
			logger.Error(err.Error())
			return
		}
	}
}

// NewOrderNO 生成订单号
func NewOrderNO() string {
	randomStr := GetRandomString(10, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	timeStampStr := strconv.FormatInt(time.Now().UnixNano()/1000, 10)
	return "gd0009" + randomStr + timeStampStr
}

// StructToMap 结构体转map by tag url
func StructToMap(obj interface{}) map[string]string {
	t := reflect.TypeOf(obj)
	resMap := map[string]string{}
	if t.Kind() != reflect.Struct {
		return resMap
	}
	v := reflect.ValueOf(obj)
	num := t.NumField()
	fmt.Println("num := ", num)
	for i := 0; i < num; i++ {
		url := t.Field(i).Tag.Get("url")
		resMap[url] = fmt.Sprintf("%v", v.Field(i).Interface())
	}
	return resMap
}

// ByteToHex 二进制转十六进制字符
func ByteToHex(b []byte) string {
	var (
		hs  = ""
		tmp = ""
	)
	for n := 0; n < len(b); n++ {
		tmp = fmt.Sprintf("%x", b[n]&0xFF)
		// log.Info("tmp := ", tmp)

		if len(tmp) == 1 {
			hs = hs + "0" + tmp
		} else {
			hs = hs + tmp
		}
	}
	return strings.ToUpper(hs)
}

// FromHexString 16机制字符串转为字节数组
func FromHexString(bytes []byte, i int) []byte {
	var j = 0
	if bytes[0] == 48 && (bytes[1] == 120 || bytes[1] == 88) {
		j += 2
		i -= 2
	}
	k := i / 2
	abyte1 := make([]byte, k)
	for l := 0; l < k; l++ {
		abyte1[l] = (byte)((hexValueOf(int(bytes[j]))<<4 | hexValueOf(int(bytes[j+1]))) & 0xff)
		j += 2
	}

	return abyte1
}

func hexValueOf(i int) int {
	if i >= 48 && i <= 57 {
		return i - 48
	}
	if i >= 97 && i <= 102 {
		return (i - 97) + 10
	}
	if i >= 65 && i <= 70 {
		return (i - 65) + 10
	} else {
		return 0
	}

}

// GenerateHash 生成密码哈希
func GenerateHash(password string, saltLength int) (hashPwd string, err error) {
	// 生成指定长度的盐值
	salt := make([]byte, saltLength)
	_, err = bcrypt.GenerateFromPassword(salt, bcrypt.DefaultCost)
	if err != nil {
		fmt.Println("生成盐值失败:", err)
		return
	}

	// 生成密码哈希
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		fmt.Println("生成密码哈希失败:", err)
		return
	}

	hashPwd = string(hash)
	return
}

func JsonEncode(value interface{}) string {
	bytes, err := json.Marshal(value)
	if err != nil {
		return ""
	}
	return string(bytes)
}

func AddMonths(t time.Time, months int) time.Time {
	return t.AddDate(0, months, 0)
}

func EndOfMonth(t time.Time) time.Time {
	return time.Date(t.Year(), t.Month()+1, 0, 23, 59, 59, 999999999, t.Location())
}

func DeleteFile(filePath string){
	os.Remove(filePath)
}

func NullStringToString(ns sql.NullString) string {
	if ns.Valid {
		return ns.String
	}
	return ""
}